﻿using System.Data.Services;
using System.Net;
using System.Threading;

namespace AstoriaPlus
{
	public static class AuthenticationInterceptorExtensions
	{
		public static IEntityChangeRules<TDataService, TDataSource, TEntity> Authenticate<TDataService, TDataSource, TEntity>(this IEntityChangeRules<TDataService, TDataSource, TEntity> entityRules, UpdateOperations operations)
			where TDataService : DataService<TDataSource>
			where TDataSource : class
			where TEntity : class
		{
			return entityRules.InterceptWith(new AuthenticationInterceptorFactory<TDataService, TDataSource, TEntity>(operations));
		}

		public static IEntityChangeRules<TDataService, TDataSource> Authenticate<TDataService, TDataSource>(this IEntityChangeRules<TDataService, TDataSource> serviceRules, UpdateOperations operations)
			where TDataService : DataService<TDataSource>
			where TDataSource : class
		{
			return serviceRules.InterceptWith(new AuthenticationInterceptorFactory<TDataService, TDataSource, object>(operations));
		}

		class AuthenticationInterceptorFactory<TDataService, TDataSource, TEntity> : IEntityInterceptorFactory<TDataService, TDataSource, TEntity>
			where TDataService : DataService<TDataSource>
			where TDataSource : class
			where TEntity : class
		{
			private UpdateOperations authorizedOperations;

			public AuthenticationInterceptorFactory(UpdateOperations authorizedOperations)
			{
				this.authorizedOperations = authorizedOperations;
			}

			class AuthenticationInterceptor : EntityInterceptor<TDataService, TDataSource, TEntity>
			{
				private UpdateOperations authorizedOperations;

				public AuthenticationInterceptor(UpdateOperations authorizedOperations)
				{
					this.authorizedOperations = authorizedOperations;
				}

				protected override void OnExecute()
				{
					if ((this.UpdateOperations & this.authorizedOperations) != 0)
					{
						var identity = Thread.CurrentPrincipal != null ? Thread.CurrentPrincipal.Identity : null;
						if (identity == null || identity.IsAuthenticated == false)
						{
							throw new DataServiceException((int)HttpStatusCode.Unauthorized, "Authentication failed.");
						}
					}
				}
			}

			public IEntityInterceptor<TDataService, TDataSource, TEntity> Create()
			{
				return new AuthenticationInterceptor(this.authorizedOperations);
			}

			IEntityInterceptor<TDataService, TDataSource> IEntityInterceptorFactory<TDataService, TDataSource>.Create()
			{
				return this.Create();
			}
		}
	}
}
